package com.yunc.modules.shop.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunc.common.exception.UCException;
import com.yunc.common.message.Message;
import com.yunc.common.redis.RedisService;
import com.yunc.common.thread.AsyncManager;
import com.yunc.common.thread.factory.AsyncFactory;
import com.yunc.common.utils.R;
import com.yunc.modules.shop.cache.ItemStoreCache;
import com.yunc.modules.shop.entity.Item;
import com.yunc.modules.shop.entity.ItemSpecs;
import com.yunc.modules.shop.entity.Order;
import com.yunc.modules.shop.entity.dto.OrderDTO;
import com.yunc.modules.shop.entity.vo.OrderItemVO;
import com.yunc.modules.shop.entity.vo.OrderVO;
import com.yunc.modules.shop.mapper.OrderMapper;
import com.yunc.modules.shop.service.IItemService;
import com.yunc.modules.shop.service.IItemSpecsService;
import com.yunc.modules.shop.service.IOrderService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 订单管理
 *
 * @author congcong tao
 * @create 2018/12/23 10:22 PM
 */
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private IItemService itemService;
    @Autowired
    private IItemSpecsService itemSpecsService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ItemStoreCache itemStoreCache;

    private static final Integer ONCE_ORDER_NUM_CREATE = 10000;

    /**
     * 订单号
     */
    private Map<String, ConcurrentLinkedQueue<String>> orderNumberQueueMap = new ConcurrentHashMap<>();

    @Override
    public Page pageInfo(Page<OrderDTO> page, Order order) {
        List<OrderDTO> orderList = orderMapper.selectEntityList(page, order);
        return page.setRecords(orderList);
    }

    /**
     * 解决办法,使用悲观锁,select xx for update
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R orderPay(OrderVO orderInfo) {
        List<OrderItemVO> orderItemList = JSON.parseArray(orderInfo.getOrderItemJson(), OrderItemVO.class);
        // 先查询
        if (orderItemList == null || orderItemList.size() == 0) {
            return R.error("未查询到商品");
        }
        for (OrderItemVO orderItem : orderItemList) {
            if (orderItem.getSpecsSeq() == null) {
                Item item = itemService.getLockItem(orderItem.getItemId());
                // 判断库存数
                if (item.getNum() >= orderItem.getNum()) {
                    // 减去库存
                    item.setNum(item.getNum() - orderItem.getNum());
                    itemService.saveOrUpdate(item);
                } else {
                    return R.error("您选择的商品库存不足!");
                }
            } else {
                // 改变库存值
                ItemSpecs itemSpecs = itemSpecsService.getLockItemSpecs(orderItem.getSpecsSeq());
                // 判断库存数
                if (itemSpecs.getStock() >= orderItem.getNum()) {
                    // 减去库存
                    itemSpecs.setStock(itemSpecs.getStock() - orderItem.getNum());
                    itemSpecsService.saveOrUpdate(itemSpecs);
                } else {
                    return R.error("您选择的商品库存不足!");
                }
            }
        }
        // 然后利用信息,生成信息到前台去支付等操作,后台去生成订单
        String orderNumber = getOneOrderNumber();
        orderInfo.setOrderNumber(orderNumber);
        AsyncManager.me().execute(AsyncFactory.insertOrder(orderInfo));
        System.out.print(DateUtil.formatDateTime(new Date()) + ":" + orderNumber);
        return R.ok().put("orderNumber", orderNumber);
    }

    /**
     * 解决办法：使用redis队列
     */
    @Override
    @Transactional(rollbackFor = {Exception.class, UCException.class})
    public R orderPayR(OrderVO orderInfo) {
        List<OrderItemVO> orderItemList = JSON.parseArray(orderInfo.getOrderItemJson(), OrderItemVO.class);
        if (orderItemList != null && orderItemList.size() > 0) {
            return R.error("未查询到商品");
        }
        for (OrderItemVO orderItemD : orderItemList) {
            if (orderItemD.getSpecsSeq() != null && orderItemD.getSpecsSeq() > 0) {
                if (!itemStoreCache.decrStore(orderItemD.getItemId() + "|" + orderItemD.getSpecsSeq(), orderItemD.getNum())) {
                    throw new UCException("占redis名额失败，等待重试");
                }
            } else {
                if (!itemStoreCache.decrStore(orderItemD.getItemId() + "", orderItemD.getNum())) {
                    throw new UCException("占redis名额失败，等待重试");
                }
            }
        }
        String orderNumber = getOneOrderNumber();
        orderInfo.setOrderNumber(orderNumber);
        Message message = new Message("CREATE_ORDER_SHOP", orderInfo);
        redisService.rpush("CREATE_ORDER_SHOP", message, 0);
        System.out.print(DateUtil.formatDateTime(new Date()) + ":" + orderNumber);
        return R.ok().put("orderNumber", orderNumber);
    }

    private synchronized String getOneOrderNumber() {
        // 当天还有剩余的订单号
        String date = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN);
        ConcurrentLinkedQueue<String> orderNumbers = orderNumberQueueMap.get(date);
        if (orderNumbers != null && !orderNumbers.isEmpty()) {
            return orderNumbers.poll();
        }
        initOrderNumber(date);
        orderNumbers = orderNumberQueueMap.get(date);
        return orderNumbers.poll();
    }

    private synchronized void initOrderNumber(String date) {
        ConcurrentLinkedQueue<String> orderNumbers = new ConcurrentLinkedQueue<>();
        orderNumberQueueMap.put(date, orderNumbers);
        // 数据库查询最小
        int minOrderIndex = 0;
        String maxOrderNumber = orderMapper.getMaxOrderNumber(date);
        if (StringUtils.isNotEmpty(maxOrderNumber)) {
            String result = maxOrderNumber.replace(date, "");
            minOrderIndex = Integer.parseInt(result);
        }
        for (int i = minOrderIndex; i <= ONCE_ORDER_NUM_CREATE; i++) {
            String result = String.format("%0" + ONCE_ORDER_NUM_CREATE.toString().length() + "d", i + 1);
            orderNumbers.offer(date + result);
        }
    }
}
